{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于Python是动态语言，根据类创建的实例可以任意绑定属性。\n",
    "\n",
    "给实例绑定属性的方法是通过实例变量，或者通过self变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "s = Student('Bob')\n",
    "s.score = 90"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是，如果Student类本身需要绑定一个属性呢？可以直接在class中定义属性，这种属性是 __类属性__ ，归Student类所有："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    name = 'Student'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当我们定义了一个类属性后，这个属性虽然归类所有，但类的所有实例都可以访问到。来测试一下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Student\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "    name = 'Student'\n",
    "    \n",
    "s = Student() # 创建实例s   \n",
    "print(s.name) # 打印name属性，因为实例并没有name属性，所以会继续查找class的name属性\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Student\n"
     ]
    }
   ],
   "source": [
    "print(Student.name) # 打印类的name属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Michael\n"
     ]
    }
   ],
   "source": [
    "s.name = 'Michael' # 给实例绑定name属性\n",
    "print(s.name) # 由于实例属性优先级比类属性高，因此，它会屏蔽掉类的name属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Student\n"
     ]
    }
   ],
   "source": [
    "print(Student.name) # 但是类属性并未消失，用Student.name仍然可以访问"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Student\n"
     ]
    }
   ],
   "source": [
    "del s.name # 如果删除实例的name属性\n",
    "print(s.name) # 再次调用s.name，由于实例的name属性没有找到，类的name属性就显示出来了"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上面的例子可以看出，在编写程序的时候，千万不要对实例属性和类属性使用相同的名字，因为相同名称的实例属性将屏蔽掉类属性，但是当你删除实例属性后，再使用相同的名称，访问到的将是类属性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 练习\n",
    "\n",
    "为了统计学生人数，可以给Student类增加一个类属性，每创建一个实例，该属性自动增加："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Students: 2\n",
      "测试通过!\n"
     ]
    }
   ],
   "source": [
    "# 方法一：\n",
    "class Student(object):\n",
    "    count = 0\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        Student.count += 1\n",
    "\n",
    "# 测试:\n",
    "if Student.count != 0:\n",
    "    print('测试失败!')\n",
    "else:\n",
    "    bart = Student('Bart')\n",
    "    if Student.count != 1:\n",
    "        print('测试失败!')\n",
    "    else:\n",
    "        lisa = Student('Bart')\n",
    "        if Student.count != 2:\n",
    "            print('测试失败!')\n",
    "        else:\n",
    "            print('Students:', Student.count)\n",
    "            print('测试通过!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Students: 2\n",
      "测试通过!\n"
     ]
    }
   ],
   "source": [
    "# 方法二：\n",
    "class Student(object):\n",
    "    count = 0\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.get_count()\n",
    " \n",
    "    def get_count(self):\n",
    "        Student.count += 1\n",
    "\n",
    "# 测试:\n",
    "if Student.count != 0:\n",
    "    print('测试失败!')\n",
    "else:\n",
    "    bart = Student('Bart')\n",
    "    if Student.count != 1:\n",
    "        print('测试失败!')\n",
    "    else:\n",
    "        lisa = Student('Bart')\n",
    "        if Student.count != 2:\n",
    "            print('测试失败!')\n",
    "        else:\n",
    "            print('Students:', Student.count)\n",
    "            print('测试通过!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
